/*
     raw os - Copyright (C)  Lingjun Chen(jorya_txj).

    This file is part of raw os.

    raw os is free software; you can redistribute it it under the terms of the 
    GNU General Public License as published by the Free Software Foundation; 
    either version 3 of the License, or  (at your option) any later version.

    raw os is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 
    without even the implied warranty of  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
    See the GNU General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program. if not, write email to jorya.txj@gmail.com
                                      ---

    A special exception to the LGPL can be applied should you wish to distribute
    a combined work that includes raw os, without being obliged to provide
    the source code for any proprietary components. See the file exception.txt
    for full details of how and when the exception can be applied.
*/

/* 	2012-9  Created by jorya_txj
  *	xxxxxx   please added here
  */


#include <raw_api.h>

#if (CONFIG_RAW_IDLE_EVENT > 0)

enum {
    /*! maximum depth of entry levels in a MSM for transition to history. */
    MSM_MAX_ENTRY_DEPTH = 4
};


static QM_STATE raw_l_msm_top_s;

static void msm_exit_to_tran_source(STM_STRUCT *me, QM_STATE *s, QM_STATE *ts);
static RAW_U16 msm_enter_history(STM_STRUCT *me, QM_STATE *hist);



/*
************************************************************************************************************************
*                                   The top of the hsm state machine
*
* Description: This function is used to init the hsm state machine to state raw_hsm_top.
*
* Arguments  :me (unused)
*                    ---------
*                    e (unused)   
*				         
* Returns			
*						
* Note(s)    	
*
*             
************************************************************************************************************************
*/
RAW_U16 raw_hsm_top(void  *me, STATE_EVENT *e) 
{
    me = me;       
    e = e; 
	
    return STM_RET_IGNORED;                 
}


void fsm_constructor(STM_STRUCT *me, stm_state_handler initial)
{

	static RAW_QMSM_VTBL vtbl_fsm = { 
		raw_fsm_init,
		raw_fsm_exceute
	};

	me->vptr  = &vtbl_fsm;
	me->state.fun = 0;
	me->temp.fun  = initial;

}



/*
************************************************************************************************************************
*                                   Init the finit state machine
*
* Description: This function is used to init the finit state machine.
*
* Arguments  :me is the state machine
*                    ---------
*                    e is the trig event   
*				         
* Returns			
*						
* Note(s)    	
*
*             
************************************************************************************************************************
*/
void raw_fsm_init(STM_STRUCT *me, STATE_EVENT *e) 
{
    RAW_U16 ret;
	
    if (me->temp.fun == 0) {

		RAW_ASSERT(0);

    }

	/*do the fsm constructor init function*/
	ret = (*me->temp.fun)(me, e);

	/*transition must happen here*/
	if (ret != STM_RET_TRAN) {
		
		RAW_ASSERT(0);
	}

	/*trig the STM_ENTRY_SIG to the new transioned state*/
    STM_TRIG(me->temp.fun, STM_ENTRY_SIG);

	/*change to new state*/
    me->state.fun = me->temp.fun; 
   
}


/*
************************************************************************************************************************
*                                   exceute the finit state machine
*
* Description: This function is used to exceute the finit state machine.
*
* Arguments  :me is the state machine
*                    ---------
*                    e is the trig event 
*				         
* Returns			
*						
* Note(s)    	
*
*             
************************************************************************************************************************
*/
void raw_fsm_exceute(STM_STRUCT *me, STATE_EVENT *e) 
{
	RAW_U16 ret;

	/*State must be stable here*/
	if (me->state.fun != me->temp.fun) {

		RAW_ASSERT(0);

	}

	/*exceute the state function with new event*/
    ret = (*me->state.fun)(me, e); 
	
    if (ret == STM_RET_TRAN) {                            

		/*exit the original state */
		STM_EXIT(me->state.fun); 
		/*enter the new state*/
		STM_ENTER(me->temp.fun); 
		/*change to new state*/
		me->state.fun = me->temp.fun; 
    }
   
}


void hsm_constructor(STM_STRUCT *me, stm_state_handler initial)
{
	static RAW_QMSM_VTBL vtbl_hsm = { 
        raw_hsm_init,
        raw_hsm_exceute
    };
    
    me->vptr  = &vtbl_hsm;
    me->state.fun = raw_hsm_top;
    me->temp.fun  = initial;

}


/*
************************************************************************************************************************
*                                   Init the hsm state machine
*
* Description: This function is used to init the finit state machine.
*
* Arguments  :me is the state machine
*                    ---------
*                    e is the trig event   
*				         
* Returns			
*						
* Note(s)    	
*
*             
************************************************************************************************************************
*/
void raw_hsm_init(STM_STRUCT *me, STATE_EVENT *e)
{
	RAW_U16 ret;
	RAW_S8 ip;

	/*Max nested state levels*/
	stm_state_handler path[STM_MAX_NEST_DEPTH];
	        
	stm_state_handler t = me->state.fun;

	if (me->temp.fun == 0) {

		RAW_ASSERT(0);

	}


	/*if state is not equal to the hsm top state, just assert*/
	if (t != STM_STATE_CAST(raw_hsm_top)) {

		RAW_ASSERT(0);
	}



	/*do the hsm constructor init function*/
	ret = (*me->temp.fun)(me, e);

	/*transition must happen here*/
	if (ret != STM_RET_TRAN) {

		RAW_ASSERT(0);
	}


	/*Becareful STM_INIT_SIG must trig t state to the nested children state, otherwise hsm crash*/
	do { 
		
		ip = 0;
		
		path[0] = me->temp.fun;

		/*Find all the father state until to raw_hsm_top*/
		STM_TRIG(me->temp.fun, STM_EMPTY_SIG);
		
		while (me->temp.fun != t) {
			++ip;
			
			if (ip >= STM_MAX_NEST_DEPTH) {
				
				RAW_ASSERT(0);
			}
			
			path[ip] = me->temp.fun;
			STM_TRIG(me->temp.fun, STM_EMPTY_SIG);
		}
		
		me->temp.fun = path[0];
		                               
		/*trig STM_ENTRY_SIG from father source state to nested children state*/
		do {        
			STM_ENTER(path[ip]);                         
			--ip;
		} while (ip >= 0);

		t = path[0];  
		/*trig the STM_INIT_SIG to the new transitioned state, if new transion happened again, then we need do int init again*/
	} while (STM_TRIG(t, STM_INIT_SIG) == STM_RET_TRAN);


	/*change to new state*/
	me->state.fun = t;                      
	me->temp.fun  = t;                     

    
}



/*
************************************************************************************************************************
*                                   Exceute the hsm state machine
*
* Description: This function is used to exceute the hsm state machine.
*
* Arguments  :me is the state machine
*                    ---------
*                    e is the trig event   
*				         
* Returns			
*						
* Note(s)    	
*
*             
************************************************************************************************************************
*/
void raw_hsm_exceute(STM_STRUCT *me, STATE_EVENT *e)
{
	stm_state_handler s;
	RAW_U16 r;
	RAW_S8 ip;
	RAW_S8 iq;
	
	stm_state_handler path[STM_MAX_NEST_DEPTH];
	
    stm_state_handler t = me->state.fun;

 	/*state must be stable here*/
	if (t != me->temp.fun) {

		RAW_ASSERT(0);
	}

    
    do {                             
        s = me->temp.fun;

		/*exceute the state function with new event*/
        r = (*s)(me, e);                         

        if (r == STM_RET_UNHANDLED) {           
			/*Move up to father state*/
            r = STM_TRIG(s, STM_EMPTY_SIG);       
        }

	/*move up to the father state to find suitable state to handle the sig*/
    } while (r == STM_RET_FATHER);

	/*if state transition happened then process it*/
    if (r == STM_RET_TRAN) {                            
       
        ip = -1;  
		
		/*save the transitioned state*/
        path[0] = me->temp.fun;            
        path[1] = t;

		/*t is the source state, and s is the state which cause new state change*/
		/*for example s is the father state of t*/
        while (t != s) {  

			/*if STM_EXIT_SIG is handled, trig STM_EMPTY_SIG to find the father state*/ 
			/*if STM_EXIT_SIG not handled , then me->temp hold the father state*/
            if (STM_TRIG(t, STM_EXIT_SIG) == STM_RET_HANDLED) {
                
            	STM_TRIG(t, STM_EMPTY_SIG);
            }

			/*move t to one father state up*/
            t = me->temp.fun;                 
        }

		/*t is the target transition state*/
        t = path[0];                           

		/*all the following code is try to find the LCA and exit from the source state to LCA state*/
		/*Be careful LCA state is either not entered not exited.*/
		/*all the father state of the target transition state is stored to path from hight to low etc, path[0] is the target transition state*/
		
        if (s == t) {      
            STM_EXIT(s);                                
            ip = 0;                            
        }
        else {
        	STM_TRIG(t, STM_EMPTY_SIG);     

            t = me->temp.fun;
            if (s == t) {                
                ip = 0;                        
            }
			
            else {
            	STM_TRIG(s, STM_EMPTY_SIG);    
                
                if (me->temp.fun == t) {
                    STM_EXIT(s);                        
                    ip = 0;                   
                }
				
                else {
                                         
                    if (me->temp.fun == path[0]) {
                        STM_EXIT(s);                    
                    }
					
                    else {
                        iq = 0;      
                        ip = 1;  
                        path[1] = t;      
                        t = me->temp.fun;                 
                                              
                        r = STM_TRIG(path[1], STM_EMPTY_SIG);
											   
                        while (r == STM_RET_FATHER) {
                            ++ip;
                            path[ip] = me->temp.fun;    
                            if (me->temp.fun == s) {      
                                iq = 1;  
                                            
								if (ip >= STM_MAX_NEST_DEPTH) {

									RAW_ASSERT(0);

								}
								
                                --ip;           
                                r = STM_RET_HANDLED;    
                            }
                            else {  
                                r = STM_TRIG(me->temp.fun, STM_EMPTY_SIG);
                            }
                        }
						
                        if (iq == 0) {   
             
							if (ip >= STM_MAX_NEST_DEPTH) {

								RAW_ASSERT(0);

							}

                            STM_EXIT(s);               

                     
                            iq = ip;
                            r = STM_RET_IGNORED;    
                            do {
                                if (t == path[iq]) {   
                                    r = STM_RET_HANDLED;
                                                         
                                    ip = iq - 1;
                                    iq = -1;
                                }
                                else {
                                    --iq; 
                                }
                            } while (iq >= 0);

                            if (r != STM_RET_HANDLED) { 
                                   
                                r = STM_RET_IGNORED;         
                                do {
                                                      
                                    if (STM_TRIG(t, STM_EXIT_SIG) == STM_RET_HANDLED) {
										STM_TRIG(t, STM_EMPTY_SIG);
                                    }
									
                                    t = me->temp.fun;    
                                    iq = ip;
                                    do {
                                        if (t == path[iq]) {
                                                       
                                            ip = iq - 1;
                                            iq = -1;
                                            r = STM_RET_HANDLED;
                                        }
                                        else {
                                            --iq;
                                        }
                                    } while (iq >= 0);
                                } while (r != STM_RET_HANDLED);
                            }
                        }
                    }
                }
            }
        }

		/*trig STM_ENTRY_SIG from LCA to transioned state*/
        for (; ip >= 0; --ip) {
            STM_ENTER(path[ip]);                        
        }
		
        t = path[0];                     
        me->temp.fun = t;                            

		/*trig the STM_INIT_SIG to the new transitioned state, if new transion happened again, then we need do it again*/
		/*Becareful STM_INIT_SIG must trig t state to the nested children state, otherwise hsm crash*/
        while (STM_TRIG(t, STM_INIT_SIG) == STM_RET_TRAN) {

			ip = 0;
			path[0] = me->temp.fun;

			/*Find all the father state until to source t state */
			STM_TRIG(me->temp.fun, STM_EMPTY_SIG);   

			while (me->temp.fun != t) {
				++ip;
				path[ip] = me->temp.fun;
				STM_TRIG(me->temp.fun, STM_EMPTY_SIG);
			}

			me->temp.fun = path[0];
			                            
			if (ip >= STM_MAX_NEST_DEPTH) {

				RAW_ASSERT(0);

			}

			/*trig STM_ENTRY_SIG from father source state to nested transition children state*/
			do {   
				STM_ENTER(path[ip]);                     
				--ip;
			} while (ip >= 0);

			/*remember the target transitoned state*/
			t = path[0];
			
		}


	}
   
	/*change to new state*/
	me->state.fun = t;                       
	me->temp.fun  = t; 
	
}


/*
************************************************************************************************************************
*                                   Test whether the current state is in state or not
* Description: This function is used to test whether the current state is in state or not.
*
* Arguments  :me is the state machine
*                    ---------
*                    state is to compared with currenet state.
*				         
* Returns			
*						
* Note(s)        if the state is the father state of current state, it also return 1.	
*
*             
************************************************************************************************************************
*/
RAW_U16 raw_is_hsm_in_state(STM_STRUCT *me, stm_state_handler state) 
{
    RAW_U16 inState = 0; 
    RAW_U16 r;

	RAW_ASSERT(me->temp.fun == me->state.fun);

    do {
        if (me->temp.fun == state) {                    
            inState = 1;
			r = STM_RET_IGNORED;
            break;                   
        }
		
        else {
            r = STM_TRIG(me->temp.fun, STM_EMPTY_SIG);
        }
		
    } while (r != STM_RET_IGNORED); 
	
    me->temp.fun = me->state.fun;        

    return inState;                                   
}


stm_state_handler raw_hsm_child_state(STM_STRUCT *me, stm_state_handler parent)
{
    stm_state_handler child = me->state.fun; 
    RAW_U8 is_confirmed = 0; 
    RAW_U16 r;

    /* establish stable state configuration */
    me->temp.fun = me->state.fun;
    do {
        /* is this the parent of the current child? */
        if (me->temp.fun == parent) {
            is_confirmed = 1; /* child is confirmed */
            r = STM_RET_IGNORED; /* break out of the loop */
        }
        else {
            child = me->temp.fun;
            r = STM_TRIG(me->temp.fun, STM_EMPTY_SIG);
        }
    } while (r != STM_RET_IGNORED); /* QHsm_top() state not reached */
    me->temp.fun = me->state.fun; /* establish stable state configuration */

    /** @post the child must be confirmed */
	RAW_ASSERT(is_confirmed != 0);

    return child; /* return the child */
}


static void msm_exit_to_tran_source(STM_STRUCT *me, QM_STATE *s, QM_STATE *ts)                                
{
    /* exit states from the current state to the tran. source state */
    while (s != ts) {
        /* exit action provided in state 's'? */
        if (s->exitAction != 0) {
            RAW_U16 r = (*s->exitAction)(me); /* execute the exit action */

            /*  is it a regular exit? */
            if (r == STM_RET_EXIT) {
                s = s->superstate; /* advance to the superstate */
            }
            /*  is it exit from a submachine? */
            else if (r == STM_RET_FATHER_SUB) {
                /* advance to the current host state of the submachie */
                s = me->temp.obj;
            }
            else {
                RAW_ASSERT(0);
            }
        }
        else {
            s = s->superstate; /* advance to the superstate */
        }
    }
}


static RAW_U16 msm_enter_history(STM_STRUCT *me, QM_STATE *hist)

{
    QM_STATE const *s;
    QM_STATE const *ts = me->state.obj; /* transition source */
    QM_STATE const *entry[MSM_MAX_ENTRY_DEPTH];
    RAW_U16 r;
    RAW_U32 i = 0;  /* transition entry path index */
   
    for (s = hist; s != ts; s = s->superstate) {
        RAW_ASSERT(s != 0);
        if (s->entryAction != 0) {
            entry[i] = s;
            ++i;
            RAW_ASSERT(i <= RAW_Q_DIM(entry));
        }
    }

    /* retrace the entry path in reverse (desired) order... */
    while (i > 0) {
        --i;
        r = (*entry[i]->entryAction)(me); /* run entry action in entry[i] */
    }

    me->state.obj = hist; /* set current state to the transition target */

    /* initial tran. present? */
    if (hist->initAction != 0) {
        r = (*hist->initAction)(me); /* execute the transition action */
    }
    else {
        r = STM_RET_NULL;
    }
    return r;
}



void msm_constructor(STM_STRUCT *me, stm_state_handler initial) 
{   
	static RAW_QMSM_VTBL vtbl_msm = { /* QMsm virtual table */
        raw_msm_init,
        raw_msm_exceute
    };
    me->vptr = &vtbl_msm;
    me->state.obj = &raw_l_msm_top_s; /* the current state (top) */
    me->temp.fun  = initial;      /* the initial transition handler */
}


RAW_U16 msm_exec_tatbl(STM_STRUCT *me, RAW_QM_TRAN_ACT_TABLE *tatbl)
{
    stm_action_handler *a;
    RAW_U16 r = STM_RET_NULL;
  
    /** @pre the transition-action table pointer must not be NULL */
    RAW_ASSERT(tatbl != 0);

    for (a = &tatbl->act[0]; *a != 0; a++) {
        r = (*(*a))(me); /* call the action through the 'a' pointer */
    }

    if (r >= STM_RET_TRAN_INIT) {
        me->state.obj = me->temp.tatbl->target; /* the tran. target */
    }
    else {
        me->state.obj = tatbl->target; /* the tran. target */
    }

    return r;
}


void raw_msm_init(STM_STRUCT *me, STATE_EVENT *e) 
{
    RAW_U16 r;
   

    /** @pre the virtual pointer must be initialized, the top-most initial
    * transition must be initialized, and the initial transition must not
    * be taken yet.
    */
    RAW_ASSERT((me->vptr != 0)
                      && (me->temp.fun != 0)
                      && (me->state.obj == &raw_l_msm_top_s));

    r = (*me->temp.fun)(me, e); /* the action of the top-most initial tran. */

    /* the top-most initial transition must be taken */
    RAW_ASSERT(r == STM_RET_TRAN_INIT);

    /* set state to the last tran. target */
    me->state.obj = me->temp.tatbl->target;

    /* drill down into the state hierarchy with initial transitions... */
    do {
        r = msm_exec_tatbl(me, me->temp.tatbl); /* execute the tran. table */
    } while (r >= STM_RET_TRAN_INIT);

}


void raw_msm_exceute(STM_STRUCT *me, STATE_EVENT *e)
{

	QM_STATE *s = me->state.obj; /* store the current state */
    QM_STATE *t = s;
    RAW_U16 r = STM_RET_FATHER;
   

    /** @pre current state must be initialized */
    RAW_ASSERT(s != 0);

    /* scan the state hierarchy up to the top state... */
    do {
        r = (*t->stateHandler)(me, e);  /* call state handler function */

        /* event handled? (the most frequent case) */
        if (r >= STM_RET_HANDLED) {
            break; /* done scanning the state hierarchy */
        }
        /* event unhandled and passed to the superstate? */
        else if (r == STM_RET_FATHER) {
            t = t->superstate; /* advance to the superstate */
        }
        /* event unhandled and passed to a submachine superstate? */
        else if (r == STM_RET_FATHER_SUB) {
            t = me->temp.obj; /* current host state of the submachie */
        }
        /* event unhandled due to a guard? */
        else if (r == STM_RET_UNHANDLED) {
            t = t->superstate; /* advance to the superstate */
        }
        else {
            /* no other return value should be produced */
            RAW_ASSERT(0);
        }
    } while (t != 0);


    /* any kind of transition taken? */
    if (r >= STM_RET_TRAN) {

        do {
            /* save the transition-action table before it gets clobbered */
            RAW_QM_TRAN_ACT_TABLE *tatbl = me->temp.tatbl;

            /* was a regular state transition segment taken? */
            if (r == STM_RET_TRAN) {
                msm_exit_to_tran_source(me, s, t);
                r = msm_exec_tatbl(me, tatbl);
            }
            /* was an initial transition segment taken? */
            else if (r == STM_RET_TRAN_INIT) {
                r = msm_exec_tatbl(me, tatbl);
            }
            /* was a transition segment to history taken? */
            else if (r == STM_RET_TRAN_HIST) {
                QM_STATE *hist = me->state.obj; /* save history */
                me->state.obj = s; /* restore the original state */
                msm_exit_to_tran_source(me, s, t);
                (void)msm_exec_tatbl(me, tatbl);
                r = msm_enter_history(me, hist);
            }
            /* was a transition segment to an entry point taken? */
            else if (r == STM_RET_TRAN_EP) {
                r = msm_exec_tatbl(me, tatbl);
            }
            /* was a transition segment to an exit point taken? */
            else if (r == STM_RET_TRAN_XP) {
                stm_action_handler act = me->state.act; /* save XP action */
                me->state.obj = s; /* restore the original state */

                msm_exit_to_tran_source(me, s, t);
                (void)msm_exec_tatbl(me, tatbl);
                r = (*act)(me); /* execute the XP action */
            }
            else {
                /* no other return value should be produced */
                RAW_ASSERT(0);
            }
            s = me->state.obj;
            t = s;

        } while (r >= STM_RET_TRAN);

    }

    else {
        /* empty */
    }


}


RAW_U16 raw_is_msm_in_state(STM_STRUCT *me, QM_STATE *state) 
{
    RAW_U16 inState = 0; /* assume that this MSM is not in 'state' */
    QM_STATE *s;

    for (s = me->state.obj; s != 0; s = s->superstate) {
        if (s == state) {
            inState = 1; /* match found, return 'true' */
            break;
        }
    }
    return inState;
}


QM_STATE *raw_msm_child_state(STM_STRUCT *me, QM_STATE *parent)
{
    QM_STATE *child = me->state.obj;
    RAW_U16 isConfirmed = 0; /* start with the child not confirmed */
    QM_STATE *s;

    for (s = me->state.obj->superstate;
         s != 0;
         s = s->superstate)
    {
        if (s == parent) {
            isConfirmed = 1; /* child is confirmed */
            break;
        }
        else {
            child = s;
        }
    }

    /** @post the child must be confirmed */
    RAW_ASSERT(isConfirmed != 0);

    return child; /* return the child */
}



#endif


